﻿using LxBasic;
using System.Text.Json.Nodes;

namespace LxCore
{
    public class JsonTextWriter : IJsonWriter
    {
        public JsonTextWriter(ITextWriter textWriter)
        {
            this.textWriter = textWriter;
        }
        ITextWriter textWriter;
        protected List<int> KeyCount = new List<int>();
        protected void WriteLeftBracket(char leftBracket)
        {
            KeyCount.Add(0);
            Write($"{leftBracket}");

        }
        protected void WriteRightBracket(char rightBracket)
        {
            Write(KeyCount[KeyCount.Count - 1] == 0 ? $"{rightBracket}" : $"\n{rightBracket}");
            KeyCount.RemoveAt(KeyCount.Count - 1);
        }
        protected void WriteItemSeparator()
        {
            int keyCount = KeyCount[KeyCount.Count - 1]++;
            if (keyCount == 0)
            {
                Write("\n");
            }
            else
            {
                Write(",\n");
            }
        }
        protected void Write(string str) => textWriter.Write(str);
        protected void WriteSepAndKey(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new InvalidDataException("key 值不能为空");
            }
            WriteItemSeparator();
            WriteString(key);
            Write(":");
        }
        protected void WriteString(string value)
        {
            Write("\"");
            Write(value);
            Write("\"");
        }
        protected void WriteObject(JsObject jsonObject, Action? writePropKV = null)
        {
            if (jsonObject is null)
            {
                Write("null");
                return;
            }
            WriteLeftBracket('{');
            writePropKV?.Invoke();
            jsonObject.ToJsonKV(this);
            WriteRightBracket('}');
        }
        public void WriteObject(JsObject jsonObject, bool withType = false)
        {
            WriteObject(jsonObject,
                withType ? () =>
                {
                    WriteString("-Type", jsonObject.GetType().FullName);
                }
            : null);
        }
        protected void WriteList<T>(JsList<T> jsonObject) where T : JsObject
        {
            if (jsonObject == null)
            {
                Write("null");
            }
            else if (jsonObject.Count == 0)
            {
                Write("[]");
            }
            else
            {
                Write("[\n");
                int lastId = jsonObject.Count - 1;
                for (int i1 = 0; i1 < lastId; i1++)
                {
                    WriteListObj(i1);
                    Write(",\n");
                }
                WriteListObj(lastId);
                Write("\n]");
            }
            void WriteListObj(int id)
            {
                if (jsonObject[id] == null) return;
                WriteObject(jsonObject[id], () =>
                {
                    WriteInt("-Index", id);
                });
            }
        }

        public void WriteBool(string key, bool value)
        {
            WriteDirect(key, value ? "true" : "false");
        }
        public void WriteInt(string key, int value)
        {
            WriteDirect(key, value.ToString());
        }

        public void WriteDouble(string key, double value)
        {
            WriteDirect(key, value.ToString());
        }

        public void WriteString(string key, string value)
        {
            WriteSepAndKey(key);
            WriteString(value);
        }

        public void WriteObject(string key, JsObject jsonObject, bool withType = false)
        {
            WriteSepAndKey(key);
            WriteObject(jsonObject, withType);
        }

        public void WriteList<T>(string key, JsList<T> jsonObject) where T : JsObject
        {
            WriteSepAndKey(key);
            WriteList(jsonObject);
        }
        public void WriteDirect(string key, string value)
        {
            WriteSepAndKey(key);
            Write(value);
        }

        public void WriteVector3d(string key, Vector3d value)
        {
            WriteDirect(key, value.ToString());
        }

        public void WriteEnum<TEnum>(string key, TEnum value) where TEnum : struct
        {
            WriteString(key, value.ToString());
        }
        protected void WriteArray<T>(T[] arr, Action<T> writeItem)
        {
            if (arr == null)
            {
                Write("null");
            }
            else if (arr.Length == 0)
            {
                Write("[0]");
            }
            else
            {
                Write($"[{arr.Length}");
                foreach (var i in arr)
                {
                    Write($",\n");
                    writeItem(i);
                }
                Write("\n]");
            }

        }

        public void WriteVector3dArray(string key, Vector3d[] value)
        {
            WriteSepAndKey(key);
            WriteArray(value, i =>
            {
                Write($"[{i.X},{i.Y},{i.Z}]");
            });
        }

        public void WriteInnFaceArray(string key, FvInnFace[] value)
        {
            WriteSepAndKey(key);
            WriteArray(value, i =>
            {
                Write("[");
                foreach (var n in i.Nodes)
                {
                    Write($"{n},");
                }
                Write($"{i.OSideCell},{i.NSideCell}]");
            });
        }

        public void WriteBouFaceArray(string key, FvBouFace[] value)
        {
            WriteSepAndKey(key);
            WriteArray(value, i =>
            {
                Write("[");
                foreach (var n in i.Nodes)
                {
                    Write($"{n},");
                }
                Write($"{i.OSideCell}]");
            });
        }

        public void WriteIndexRange(string key, IndexRange value)
        {
            WriteSepAndKey(key);
            Write($"[{value.Start},{value.End}]");
        }
        public void WriteIntArray(string key, int[] value)
        {
            WriteSepAndKey(key);
            WriteArray(value, i =>
            {
                Write($"{i}");
            });
        }

        public void WriteDoubleArray(string key, double[] value)
        {
            WriteSepAndKey(key);
            WriteArray(value, i =>
            {
                Write($"{i}");
            });
        }

        public void WriteVector2d(string key, Vector2d value)
        {
            WriteDirect(key, value.ToString());
        }

        public void WriteVector2dArray(string key, Vector2d[] value)
        {
            WriteSepAndKey(key);
            WriteArray(value, i =>
            {
                Write($"[{i.X},{i.Y}]");
            });
        }

        public void WriteMonitorSelector(string key, int id)
        {
            WriteSepAndKey(key);
            WriteLeftBracket('[');
            foreach (var zc in LxSim.Sim.Conditions.ZoneConds)
            {
                if (zc.InnerCond.MonitorSelector[id])
                {
                    WriteItemSeparator();
                    WriteString(zc.Name);
                }
                foreach (var bc in zc.BoundConds)
                {
                    if (bc.MonitorSelector[id])
                    {
                        WriteItemSeparator();
                        WriteString($"{zc.Name}:{bc.Name}");
                    }
                }
            }
            WriteRightBracket(']');
        }

        public void WriteGraphSelector(string key, int id)
        {
            WriteSepAndKey(key);
            WriteLeftBracket('[');
            foreach (var zc in LxSim.Sim.Conditions.ZoneConds)
            {
                if (zc.InnerCond.GraphSelector[id])
                {
                    WriteItemSeparator();
                    WriteString(zc.Name);
                }
                foreach (var bc in zc.BoundConds)
                {
                    if (bc.GraphSelector[id])
                    {
                        WriteItemSeparator();
                        WriteString($"{zc.Name}:{bc.Name}");
                    }
                }
            }
            WriteRightBracket(']');
        }

    }
}
